package com.aiwu.memory;

import android.annotation.SuppressLint;
import android.text.TextUtils;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.widget.EditText;

import androidx.annotation.IntDef;

import com.aiwu.bean.MemoryBean;
import com.aiwu.library.AiwuSimulator;
import com.aiwu.library.util.ClipboardUtil;
import com.aiwu.library.util.LogUtil;
import com.aiwu.library.util.ToastUtil;

import org.citra.citra_emu.NativeLibrary;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import rx.Observable;
import rx.Single;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;

/**
 * 内存单例管理类
 */
public class MemoryManager implements OnActionListener {
    private static final String TAG = MemoryManager.class.getName();

    @IntDef({STEP_FIRST, STEP_SEARCHING, STEP_RESULT_MANY, STEP_RESULT_LIST, STEP_BACK_GAME})
    public @interface Step {
    }

    //初始
    public static final int STEP_FIRST = 0;
    //搜索中
    public static final int STEP_SEARCHING = 1;
    //结果太多
    public static final int STEP_RESULT_MANY = 2;
    //结果少 列出来
    public static final int STEP_RESULT_LIST = 3;
    //回游戏改数值
    public static final int STEP_BACK_GAME = 4;

    @IntDef({SEARCH_TYPE_NORMAL, SEARCH_TYPE_FUZZY})
    public @interface SearchType {
    }

    //普通搜索
    public static final int SEARCH_TYPE_NORMAL = 0;
    //模糊搜索
    public static final int SEARCH_TYPE_FUZZY = 1;

    @IntDef({SCAN_TYPE_FIRST, SCAN_TYPE_BIGGER, SCAN_TYPE_SMALLER, SCAN_TYPE_CHANGED, SCAN_TYPE_NO_CHANGE})
    public @interface ScanType {
    }

    //region模糊搜索 值不能改，跟C里面的对应
    public static final int SCAN_TYPE_FIRST = -1;//初始 全部内存
    public static final int SCAN_TYPE_BIGGER = 2;//变大
    public static final int SCAN_TYPE_SMALLER = 4;//变小
    public static final int SCAN_TYPE_CHANGED = 1;//有变化
    public static final int SCAN_TYPE_NO_CHANGE = 0;//无变化
    //endregion

    //结果数量阈值
    public static final int RESULT_NUM_THRESHOLD = 50;
    //当前步骤
    @Step
    private int step = STEP_FIRST;

    //搜索类型
    @SearchType
    private int searchType = SEARCH_TYPE_NORMAL;
    //深度搜索
    private boolean depthSearch = false;

    //上个搜索值
    private String lastSearchValue = "";
    //内存数量 数量多的时候有值
    private int memorySize;
    //内存列表 数量少的时候有值
    private List<MemoryBean> memoryList;
    //锁定内存列表
    private Map<Long, Long> lockMemoryMap;
    //搜索的订阅
    private Subscription searchSubscription;
    private List<OnActionCallback> onActionCallbacks;
    private MemoryActView memoryActView;

    @Step
    public int getStep() {
        return step;
    }

    /**
     * 恢复步骤
     */
    public void restoreStep() {
        if (memoryActView == null) {
            return;
        }
        switch (step) {
            default:
            case STEP_FIRST:
                memoryActView.showFirstFragment();
                break;
            case STEP_SEARCHING:
                memoryActView.showLoadingFragment();
                break;
            case STEP_RESULT_MANY:
                memoryActView.showResultManyFragment();
                break;
            case STEP_RESULT_LIST:
                memoryActView.showResultListFragment();
                break;
            case STEP_BACK_GAME:
                switch (searchType) {
                    default:
                    case SEARCH_TYPE_NORMAL:
                        memoryActView.showNormalBackGameFragment();
                        break;
                    case SEARCH_TYPE_FUZZY:
                        memoryActView.showFuzzyBackGameFragment();
                        break;
                }
                break;
        }
    }

    /**
     * 改变开始步骤
     */
    private void change2StepFirst() {
        step = STEP_FIRST;
        if (memoryActView != null) {
            memoryActView.showFirstFragment();
        }
    }

    /**
     * 普通搜索
     * 改变到搜索中步骤
     */
    private void change2StepSearchingNormal(String searchValue) {
        lastSearchValue = searchValue;
        change2StepSearchingFuzzy();
    }

    /**
     * 模糊搜索
     * 改变到搜索中步骤
     */
    private void change2StepSearchingFuzzy() {
        step = STEP_SEARCHING;
        closeKeyboard();
        if (memoryActView != null) {
            memoryActView.showLoadingFragment();
        }
    }

    /**
     * 改变到结果很多
     */
    private void change2StepResultMany(int resultSize) {
        step = STEP_RESULT_MANY;
        memorySize = resultSize;
        if (memoryActView != null) {
            memoryActView.showResultManyFragment();
        }
    }

    /**
     * 改变到结果列表
     */
    private void change2StepResultList(List<Long> resultList) {
        step = STEP_RESULT_LIST;
        memoryList = new ArrayList<>();
        for (int i = 0; i < resultList.size(); i += 2) {
            long address = resultList.get(i);
            long value = resultList.get(i + 1);
            MemoryBean memoryBean = new MemoryBean();
            memoryBean.setAddress(address);
            memoryBean.setValue(value);
            memoryBean.setSelected(true);
            memoryList.add(memoryBean);
        }
        if (memoryActView != null) {
            memoryActView.showResultListFragment();
        }
    }

    /**
     * 改变到回游戏修改值
     */
    private void change2StepBackGame() {
        step = STEP_BACK_GAME;
        if (memoryActView != null) {
            memoryActView.finishAct();
        }
    }

    @Override
    public void showKeyboard(EditText editText, boolean search, KeyBoardOffsetCallback keyBoardOffsetCallback) {
        if (memoryActView == null)
            return;
        memoryActView.showKeyboardFragment(search, keyBoardOffsetCallback);
        memoryActView.getKeyboardFragment().bindEditText(editText);
        if (onActionCallbacks != null) {
            for (OnActionCallback callback : onActionCallbacks) {
                callback.onShowKeyboard();
            }
        }
    }

    @Override
    public void closeKeyboard() {
        if (memoryActView == null)
            return;
        memoryActView.hideKeyboardFragment();
        if (onActionCallbacks != null) {
            for (OnActionCallback callback : onActionCallbacks) {
                callback.onCloseKeyboard();
            }
        }
    }

    @Override
    public void changeSearchType(@SearchType int type) {
        if (searchType == type) {
            return;
        }
        switch (type) {
            default:
            case SEARCH_TYPE_NORMAL:
                reset();
                break;
            case SEARCH_TYPE_FUZZY:
                reset();
                searchMemoryFuzzy(SCAN_TYPE_FIRST);
                break;
        }
        searchType = type;
    }

    @Override
    public void switchDepthSearch() {
        depthSearch = !depthSearch;
        reset();
    }

    @Override
    public void searchMemoryNormal(String value) {
        int valueInt = getIntValue(value);
        int lastSearchValueInt = getIntValue(lastSearchValue);
        if (!TextUtils.isEmpty(lastSearchValue) && depthSearch) {//深度搜索 除第一次外
            if (valueInt > lastSearchValueInt) {
                searchMemoryFuzzy(SCAN_TYPE_BIGGER);
            } else if (valueInt < lastSearchValueInt) {
                searchMemoryFuzzy(SCAN_TYPE_SMALLER);
            } else {
                searchMemoryFuzzy(SCAN_TYPE_NO_CHANGE);
            }
            lastSearchValue = value;
            return;
        }
        searchSubscription = Single.fromCallable(() -> {
                    int startAddr = GetSigned(Hex2Long("00000000"));
                    int stopAddr = GetSigned(Hex2Long("FFFFFFFF"));
                    return NativeLibrary.searchMemory(startAddr, stopAddr, 0, SEARCH_TYPE_NORMAL, 0, valueInt);
                }).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .doOnSubscribe(() -> {
                    change2StepSearchingNormal(value);
                })
                .subscribe(results -> {
                    int resultSize = results.length / 2;
                    if (resultSize == 0) {
                        ToastUtil.toast("未搜索到数据,请重新搜索!");
                        reset();
                    } else if (resultSize > MemoryManager.RESULT_NUM_THRESHOLD) {
                        change2StepResultMany(resultSize);
                    } else {
                        List<Long> resultList = new ArrayList<>();
                        for (int item : results) {
                            resultList.add(MemoryManager.GetUnsigned(item));
                        }
                        change2StepResultList(resultList);
                    }
                }, throwable -> {
                    throwable.fillInStackTrace();
                    ToastUtil.toast("搜索内存出错");
                });
    }

    @Override
    public void searchMemoryFuzzy(@ScanType int scanType) {
        searchSubscription = Single.fromCallable(() -> {
                    int startAddr = GetSigned(Hex2Long("00000000"));
                    int stopAddr = GetSigned(Hex2Long("FFFFFFFF"));
                    return NativeLibrary.searchMemory(startAddr, stopAddr, 0, SEARCH_TYPE_FUZZY, scanType, 0);
                })
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .doOnSubscribe(this::change2StepSearchingFuzzy)
                .subscribe(results -> {
                    int resultSize = results.length / 2;
                    if (resultSize == 0) {
                        ToastUtil.toast("未搜索到数据,请重新搜索!");
                        reset();
                    } else if (resultSize > MemoryManager.RESULT_NUM_THRESHOLD) {
                        change2StepResultMany(resultSize);
                    } else {
                        List<Long> resultList = new ArrayList<>();
                        for (int item : results) {
                            resultList.add(MemoryManager.GetUnsigned(item));
                        }
                        change2StepResultList(resultList);
                    }
                }, throwable -> {
                    throwable.printStackTrace();
                    ToastUtil.toast("搜索内存出错");
                });
    }

    @Override
    public void reset() {
        //取消搜索订阅
        if (searchSubscription != null && !searchSubscription.isUnsubscribed()) {
            searchSubscription.unsubscribe();
        }
        searchType = SEARCH_TYPE_NORMAL;
        lastSearchValue = "";
        NativeLibrary.resetSearchResults();
        change2StepFirst();
        if (memoryActView != null)
            memoryActView.getKeyboardFragment().cleanEditText();
        closeKeyboard();
        //清空一些临时对象
        lockMemoryMap = null;
        memoryList = null;
    }

    @Override
    public void backGame() {
        change2StepBackGame();
    }

    @Override
    public void modifyMemory(String value) {
        if (memoryList == null || memoryList.isEmpty()) {
            return;
        }
        boolean selected = false;
        for (MemoryBean bean : memoryList) {
            if (bean.isSelected()) {
                selected = true;
                break;
            }
        }
        if (!selected) {
            ToastUtil.toast("请选择要修改的值");
            return;
        }
        Observable.from(memoryList)
                .filter(MemoryBean::isSelected)
                .subscribeOn(Schedulers.io())
                .doOnNext(memoryBean -> {
                    int addressInt = GetSigned(memoryBean.getAddress());
                    int valueInt = getIntValue(value);
                    NativeLibrary.writeMemory(addressInt, 0, valueInt);
                    memoryBean.setValue(valueInt);
                    if (lockMemoryMap != null && lockMemoryMap.containsKey(memoryBean.getAddress())) {
                        lockMemoryMap.put(memoryBean.getAddress(), memoryBean.getValue());
                    }
                })
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(memoryBean -> {
                }, throwable -> {
                    throwable.printStackTrace();
                    ToastUtil.toast("修改出错");
                }, () -> {
                    ToastUtil.toast("修改成功，请继续游戏");
                    if (memoryActView != null) {
                        memoryActView.finishAct();
                    }
                });
    }

    @Override
    public void addLockMemory(MemoryBean memoryBean) {
        if (lockMemoryMap == null) {
            lockMemoryMap = new HashMap<>();
        }
        lockMemoryMap.put(memoryBean.getAddress(), memoryBean.getValue());
    }

    @Override
    public void removeLockMemory(MemoryBean memoryBean) {
        if (lockMemoryMap != null) {
            lockMemoryMap.remove(memoryBean.getAddress());
        }
    }

    @Override
    public void createCheat() {
        if (memoryList == null || memoryList.isEmpty()) {
            return;
        }
        boolean selected = false;
        for (MemoryBean bean : memoryList) {
            if (bean.isSelected()) {
                selected = true;
                break;
            }
        }
        if (!selected) {
            ToastUtil.toast("请选择要生成的值");
            return;
        }
        Single.fromCallable(() -> {
                    StringBuilder sbCheat = new StringBuilder();
                    String lastAddressOffsetHex = "";
                    for (MemoryBean bean : memoryList) {
                        if (!bean.isSelected()) {
                            continue;
                        }
                        String addressOffsetHex = getAddressOffset(bean);
                        long addressOffsetLong = Hex2Long(addressOffsetHex);
                        LogUtil.d(TAG, "地址偏移：" + addressOffsetHex);
                        if (!addressOffsetHex.equalsIgnoreCase(lastAddressOffsetHex)) {
                            lastAddressOffsetHex = addressOffsetHex;
                            sbCheat.append("D3000000")
                                    .append(" ")
                                    .append(addressOffsetHex)
                                    .append("\n");
                        }
                        long address = bean.getAddress();
                        String newAddressHex = Long2Hex(address - addressOffsetLong, false);
                        sbCheat.append(newAddressHex)
                                .append(" ")
                                .append(bean.getValueStr(false))
                                .append("\n");
                    }
                    sbCheat.deleteCharAt(sbCheat.length() - 1);
                    return sbCheat.toString();
                }).subscribeOn(Schedulers.computation())
                .observeOn(AndroidSchedulers.mainThread())
                .doOnSuccess(s -> {
                    if (memoryActView != null) {
                        memoryActView.finishAct();
                    }
                })
                .delay(500, TimeUnit.MILLISECONDS)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(cheat -> {
                    LogUtil.d(TAG, "金手指：" + cheat);
                    ClipboardUtil.copyText(cheat, true);
                    AiwuSimulator.showCheatInsertDialog(true);
                }, throwable -> {
                    throwable.printStackTrace();
                    ToastUtil.toast("生成金手指出错");
                });
    }

    /**
     * 获取地址偏移
     *
     * @return
     */
    private String getAddressOffset(MemoryBean bean) {
        long address = bean.getAddress();
        String currentOffsetHex = Long2Hex(address, false).substring(0, 2) + "252525";
        while (true) {
            if (Hex2Long(currentOffsetHex) > address) {
                currentOffsetHex = "00" + currentOffsetHex.substring(0, 6);
                if (currentOffsetHex.equalsIgnoreCase("00000000")) {
                    break;
                }
            } else {
                break;
            }
        }
        return currentOffsetHex;
    }

    /**
     * 开始锁内存线程
     */
    private void startLockMemoryThread() {
        Observable.interval(1, TimeUnit.SECONDS)
                .subscribe(aLong -> {
                    if (lockMemoryMap == null || lockMemoryMap.isEmpty()) {
                        return;
                    }
                    for (Map.Entry<Long, Long> entry : lockMemoryMap.entrySet()) {
                        int addressInt = GetSigned(entry.getKey());
                        int valueInt = GetSigned(entry.getValue());
                        NativeLibrary.writeMemory(addressInt, 0, valueInt);
                    }
                }, throwable -> {
                    throwable.printStackTrace();
                    startLockMemoryThread();
                });
    }

    public void addOnActionCallback(OnActionCallback onActionCallback) {
        if (onActionCallbacks == null) {
            onActionCallbacks = new ArrayList<>();
        }
        onActionCallbacks.add(onActionCallback);
    }

    public void removeOnActionCallback(OnActionCallback onActionCallback) {
        if (onActionCallbacks != null) {
            onActionCallbacks.remove(onActionCallback);
        }
    }

    public void setMemoryActView(MemoryActView memoryActView) {
        this.memoryActView = memoryActView;
    }

    public void removeMemoryActView() {
        this.memoryActView = null;
    }

    public String getLastSearchValue() {
        return lastSearchValue;
    }

    public int getMemorySize() {
        return memorySize;
    }

    public List<MemoryBean> getMemoryList() {
        return memoryList == null ? new ArrayList<>() : memoryList;
    }

    public int getSearchType() {
        return searchType;
    }

    public boolean isDepthSearch() {
        return depthSearch;
    }

    /**
     * 把String转成int 用于传入C方法
     *
     * @param value
     * @return
     */
    public static int getIntValue(String value) {
        //整数型
        try {
            int valueInt = Integer.parseInt(value);
            if (valueInt < 0) {
                return GetSigned(valueInt + MemoryBean.NEGATIVE_FLAG);
            } else {
                return valueInt;
            }
        } catch (NumberFormatException ignore) {
            LogUtil.d(TAG, "值不是整数型");
        }
        //浮点型
        try {
            float valueFloat = Float.parseFloat(value);
            if (valueFloat < 0) {
                return GetSigned(Float.floatToIntBits(valueFloat) + MemoryBean.NEGATIVE_FLAG);
            } else {
                return Float.floatToIntBits(valueFloat);
            }
        } catch (NumberFormatException ignore) {
            LogUtil.d(TAG, "值不是浮点型");
        }
        return 0;
    }

    /**
     * 值如果是float或者int就合法
     *
     * @param value 是否合法
     * @return 值是否合法
     */
    public static boolean isValueValid(String value) {
        try {
            Integer.parseInt(value);
            return true;
        } catch (NumberFormatException ignore) {
        }
        try {
            Float.parseFloat(value);
            return true;
        } catch (NumberFormatException ignore) {
        }
        return false;
    }

    public static String Long2Hex(long value, boolean prefix) {
        char[] hexChars = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 8; ++i) {
            sb.insert(0, hexChars[(int) (value & 0xF)]);
            value >>= 4;
        }
        if (prefix)
            sb.insert(0, "0x");
        return sb.toString();
    }

    public static long Hex2Long(String text) {
        long value = 0;
        for (int i = 0; i < text.length(); ++i) {
            int v = Character.digit(text.charAt(i), 16);
            if (v != -1) {
                value = value << 4 | v;
            } else {
                break;
            }
        }
        return value;
    }

    public static long GetUnsigned(int signed) {
        return signed < 0 ? 2 * (long) Integer.MAX_VALUE + 2 + signed : signed;
    }

    public static int GetSigned(long unsigned) {
        return (int) (unsigned > Integer.MAX_VALUE ? unsigned - 2 * (long) Integer.MAX_VALUE - 2 : unsigned);
    }

    @SuppressLint("ClickableViewAccessibility")
    public static void disableShowInput(EditText editText, boolean search, KeyBoardOffsetCallback keyBoardOffsetCallback) {
        editText.setShowSoftInputOnFocus(false);
        editText.setOnTouchListener((v, event) -> {
            if (event.getAction() == MotionEvent.ACTION_DOWN) {
                editText.requestFocus();
                MemoryManager.getInstance().showKeyboard(editText, search, keyBoardOffsetCallback);
            }
            return true;
        });
        editText.setOnClickListener(v -> {
            editText.requestFocus();
            MemoryManager.getInstance().showKeyboard(editText, search, keyBoardOffsetCallback);
        });
    }

    private MemoryManager() {
        startLockMemoryThread();
    }

    public static MemoryManager getInstance() {
        return Holder.INSTANCE;
    }

    private static class Holder {
        private static MemoryManager INSTANCE = new MemoryManager();
    }

    public interface OnActionCallback {
        void onShowKeyboard();

        void onCloseKeyboard();
    }

    public interface KeyBoardOffsetCallback {
        boolean setKeyBoardOffset(ViewGroup.MarginLayoutParams layoutParams);
    }
}
